/***************************************************************************************************
  *Copyright(C),2010-2016,Sumscope
  *FileName	:  StringPtrMap.cpp
  *Author	:  scofined.qi
  *Version	:  1.0
  *Date		:  2016/09/04
  *Desc		:  //用以实现 string -> void* 的哈希表
  *Relation :  
  *Others	:	1.底层使用最快的哈希函数XXH32
				2.使用list管理的对象池，用于快速调整哈希表尺寸所引发的内存分配释放
				3.使用4kb边界的内置字符串分配器来避免大量字符串对象的释放过程
					内部字符串操作使用其指针，避免了字符串移动复制的开销
  *Function :  
  *History	:  
***************************************************************************************************/
#include "StringPtrMap.h"
#include <list>
#include <map>
#include <set>
#include <core/container/SHash.h>
#include <core/container/KeyHashMap.h>
#include <core/allocator/allocator_pool_manager.h>
#include <core/container/hash_prime.h>
namespace{
	////////////////////////////////////////////////////////////////////////////////
	template<class Type>
	class ObjectPtrPool {
		std::list<Type*>		m_unused;
		int				m_capcity;		// 默认-1,表示全部缓存
		int				m_allocated;
		bool			m_usepool;
	public:
		ObjectPtrPool(int capcity=-1):m_capcity(capcity),m_allocated(0),m_usepool(true){}
		~ObjectPtrPool(){
			auto it = m_unused.begin();
			for (;it!=m_unused.end();++it)
			{
				delete *it;
			}
		}
		virtual void OnEntry(Type* entry, int slot)
		{

		}
		void SetUsePool(bool usePool){m_usepool=usePool;}
		int GetCount()const{return m_unused.size();}
		int GetCapcity()const{return m_capcity;}
		void SetCapcity(int capcity){m_capcity=capcity;}
		Type* Alloc(){
			if(!m_usepool)
				return new Type();
			Type* type = 0;
			if(!m_unused.empty())
			{
				type = m_unused.front();
				m_unused.pop_front();
			}else
				type = new Type();
			m_allocated++;
			return type;
		}
		void Free(Type* t){
			if(!m_usepool)
			{	
				delete t;
				return;
			}
			//BASSERT(t);
			m_allocated--;
			if(m_capcity>0 && (int)m_unused.size()>=m_capcity)
			{
				delete t;
			}else
				m_unused.push_back(t);
		}
	};
}

class StringPtrMapImpl{
	typedef const char* StringId;
	struct Key{
		StringId		id;
		const void*		ptr;
	public:
		Key():id(0),ptr(0){}
		Key(const char* c,const void* p):id(c),ptr(p){}
		Key(const Key& k):id(k.id),ptr(k.ptr){}
		unsigned int GetHash(){return qb::base::GetFastHash(id,strlen(id),0);}
		bool operator==(const Key& k){return id==k.id || strcmp(id,k.id)==0;}
	};
	struct  Entry{							// 
		Entry*			hashprev;			// 哈西插槽的双链表的后一个
		Entry*			hashnext;			// 哈西插槽的双链表的前一个
		Key				key;				// 用于标志
		Entry():hashnext(0),hashprev(0){}
	};
	class Collector : public qb::base::KeyHashMap<Entry, Key>::Listener{
		ObjectPtrPool<Entry>*	m_cache;
	public:
		Collector(ObjectPtrPool<Entry>* pcache) :m_cache(pcache){}
		void Enum(qb::base::KeyHashMap<Entry, Key>& hashmap){ hashmap.Enum(this); }
		virtual void OnEntry(Entry* entry, int slot){ m_cache->Free(entry); }
	};

	typedef qb::base::HeapAllocator Allocator;
	Allocator*				m_string_buf;	// 
	qb::base::KeyHashMap<Entry, Key>	m_hashmap;		// 
	ObjectPtrPool<Entry>	m_cache;		// 
	int						m_nexpand;		// 扩展次数
public:
	StringPtrMapImpl() :m_nexpand(0)
	{
		m_string_buf = new Allocator("StringPtrMap");
		m_hashmap.Build(43853);
		m_cache.SetUsePool(false);
	}
	~StringPtrMapImpl()
	{
		//BS_METRICS("");
		Collector collector(&m_cache);
		collector.Enum(m_hashmap);
		m_hashmap.Clear();
		if (m_string_buf)
			delete m_string_buf;
	}
	void SetAt(const char* name,void* ptr)
	{
		Key key(name,ptr);
		unsigned int hash = key.GetHash();
		Entry* et=m_hashmap.Find(key,hash);
		if(et==0)
		{
			int len = strlen(name);
			char* buf = (char*)m_string_buf->Alloc(len + 1);
			memcpy(buf, name, len);
			buf[len] = 0;

			key.id = buf;
			et = NewEntry(key);
			m_hashmap.Insert(et,hash);
		}else
		{
			et->key.ptr=ptr;
		}
	}
	bool Lookup(const char* name,void*& ptr)
	{
		Key key(name,0);
		unsigned int hash = key.GetHash();
		Entry* et=m_hashmap.Find(key,hash);
		if(et)
			ptr=(void*)et->key.ptr;
		return et!=0;
	}
	void RemoveAll()
	{
		//BS_METRICS("");
		Collector collector(&m_cache);
		collector.Enum(m_hashmap);
		m_hashmap.Clear();
		m_string_buf->Reset();
	}
	void Expand(int capcity){ m_hashmap.Expand(capcity); m_nexpand++; }
	void ExpandByRatio(double ratio)
	{
		int count = m_hashmap.GetCount();
		ratio = ratio<0.4?0.4:ratio;
		ratio = ratio>20?20:ratio;
		int length = (count<10949?10949:count)/ratio;
		m_hashmap.Expand(length);
	}
	void InitHashTable(int capcity)
	{
		//BS_METRICS("InitHashTable:count:%d length:%d cap:%d",m_hashmap.GetCount(),m_hashmap.GetLength(),capcity);
		m_hashmap.Expand(capcity);
	}

	int GetLength()const{return m_hashmap.GetLength();}
	double GetHashRatio()const{return m_hashmap.GetCount()*1.0/m_hashmap.GetLength();}
	int  GetBytes()const
	{
		int bytes = sizeof(*this);
		bytes+=m_string_buf->GetBytes();
		bytes+=m_hashmap.GetLength()*sizeof(Entry*);
		bytes+=m_hashmap.GetCount()*sizeof(Entry);
		bytes+=m_cache.GetCount()*(sizeof(Entry*)*3+sizeof(Entry));
		return bytes;
	}
	void DumpStatus(int mode = 0)const
	{
	}
protected:
	Entry* NewEntry(const Key& k)
	{
		Entry* et=m_cache.Alloc();
		et->hashprev=et->hashnext=0;
		et->key=k;
		return et;
	}
};

#include <core/container/LinkedHashMap.h>
#include "BondKeyHashMap.h"
class BondKeyMapImpl {
	typedef qb::base::F14LinkedChunk<ComBondKeyCompact, BondIndex> BondKeyF14Cache;
	typedef qb::base::F14LinkedChunk<BondCodeCompact, BondIndex> BondCodeF14Cache;

	typedef qb::base::CacheLineArray<ComBondKeyCompact, BondIndex> BondKeyCacheLine;
	typedef BondKeyHashMap<ComBondKeyCompact, BondKeyParser, BondKeyF14Cache> FlinkedBondKeyMap;
	typedef BondKeyHashMap<ComBondKeyCompact, BondKeyCacheLine> CachedBondKeyMap;
	typedef BondKeyHashMap<BondCodeCompact, BondCodeParser, BondCodeF14Cache> FlinkedBondCodeMap;

	FlinkedBondKeyMap		m_keymap;
	FlinkedBondCodeMap		m_codemap;
	bool					m_askey;
public:
	BondKeyMapImpl(bool asKey) :m_askey(asKey)
	{
		/*
			[内存优化][scofined.qi]
			经过测试,该哈希表的哈希插槽数组长度为2^13,性能和内存综合最好
			因此BondKeyMapImpl中,Expand的参数为13,表示长度为2^13

			初略估计,2^13*16*2=262144
			16是一个节点能放置的最多的键值对个数,2是节点链表长度
		*/
		if (asKey)
			m_keymap.Expand(13);
		else
			m_codemap.Expand(13);
	}
	~BondKeyMapImpl() {}
	void SetAt(const char* key, void* ptr)
	{
		static_assert(sizeof(void*) == sizeof(intptr_t), "assert failed:sizeof(void*) == sizeof(int)");
		BondIndex bid = (intptr_t)ptr;
		if (m_askey)
			m_keymap.AddBondKey(key, strlen(key), bid);
		else
			m_codemap.AddBondKey(key, strlen(key), bid);
	}
	bool RemoveKey(const char* key)
	{
		if (m_askey)
			return m_keymap.EraseBondKey(key, strlen(key));

		return m_codemap.EraseBondKey(key, strlen(key));
	}
	void RemoveAll()
	{
		if (m_askey)
			m_keymap.RemoveAll();
		else
			m_codemap.RemoveAll();
	}
	bool Lookup(const char* key, void*& ptr)
	{
		BondIndex bid = m_askey ? (m_keymap.GetBondIndex(key, strlen(key))) :
			m_codemap.GetBondIndex(key, strlen(key));

		if (bid >= 0)
			ptr = (void*)bid;
		return bid >= 0;
	}
	void InitHashTable(int capcity)
	{
		// 不需要重新哈希
		//m_bondkeys.Expand(capcity);
	}
	int GetLength()const {
		return m_askey ? m_keymap.GetLength() : m_codemap.GetLength();
	}
	double GetHashRatio()const {
		return m_askey ? m_keymap.GetLoadRatio() : m_codemap.GetLoadRatio();
	}
	int GetBytes()const { return m_askey ? m_keymap.GetBytes() : m_codemap.GetBytes(); }
	void ExpandByRatio(double ratio)
	{

	}
	void Expand(int length)
	{

	}
	void DumpStatus(int mode)const
	{

	}

};
// 
typedef std::map<std::string, int> StringIntMap;
typedef std::set<int> IntSet;

//static_assert(sizeof(std::string) == 24, "sizeof(std::string) == 24");
//static_assert(sizeof(StringIntMap::value_type) == 28, "sizeof(std::string) == 24");
//static_assert(sizeof(StringIntMap::mapped_type) == 4, "sizeof(std::string) == 24");
//static_assert(sizeof(StringIntMap::_Node) == 44, "sizeof(std::string) == 24");
//键
////////////////////////////////////////////////////////////////////////////////
StringPtrMap::StringPtrMap(kKeyMode mode) :
m_mode(mode),
m_impl(0)
{
	m_impl = new KeyMapImpl(mode==kKeyModeAsKey);
}

StringPtrMap::~StringPtrMap()
{
	if (m_impl) { delete m_impl; m_impl = 0; }
}

void StringPtrMap::SetAt( const char* key,void* ptr )
{
	m_impl->SetAt(key, ptr);
}

bool StringPtrMap::Lookup( const char* key,void*& ptr )
{
	return m_impl->Lookup(key, ptr);
}
bool StringPtrMap::RemoveKey(const char* key)
{
	return m_impl->RemoveKey(key);
}
void StringPtrMap::RemoveAll()
{
	//BS_METRICS("RemoveAll");
	m_impl->RemoveAll();
}

void StringPtrMap::InitHashTable( int capcity )
{
	//BS_METRICS("InitHashTable:%d",capcity);
	m_impl->InitHashTable(capcity);
}

void StringPtrMap::DumpStatus(int mode)const
{
}
////////////////////////////////////////////////////////////////////////////////
double StringPtrMap::GetHashRatio()const
{
	return m_impl->GetHashRatio();
}
int StringPtrMap::GetLength()const
{
	return m_impl->GetLength();
}
int StringPtrMap::GetBytes()const{
	return m_impl->GetBytes();
}
void StringPtrMap::Expand( int length )
{
	m_impl->Expand(length);
}
void StringPtrMap::ExpandByRatio(double ratio)
{
	m_impl->ExpandByRatio(ratio);
}
